#include "XSystem/XCore.h"
#include "GMain.h"

// Initialize the window
GMain::GMain(XResourcesManager *data) : XWindow(data)
{
	// Default values
    x_positionX = 0;
    x_positionY = 0;
    x_width = data->GetInt("resolutionX");
    x_height = data->GetInt("resolutionY");
	x_static = true;

	x_app = x_data->GetApp();

	m_mapLoaded = false;

	m_chatActivated = false;

	x_charDirection = 5;
	x_charAnimationNumber = 1;
	x_charMovementNumber = 0;

	x_lastTargetX = 0;
	x_lastTargetY = 0;
	x_isDoneMoving = true;

	m_selector.SetImage(x_data->GetImage("gui/selector.png"));

	// Create the widgets

	// Chat history and chatbar
	x_widgets["textchat"] = new XTextField(data, 25, x_height-30, 500);
	x_widgets["scrollhistory"] = new XLabelScroll(data, 25, x_height-30, 500, 5);

	// Charname labels
	x_widgets["labelcharnameclient"] = new XLabel(data, (x_width/2), 320, "");
	x_widgets["labelcharnameclient"]->SetBackground(true);
	x_widgets["labelcharnameclient"]->SetShadow(true);
	x_widgets["labelcharnameclient"]->SetAlign("center");
	x_widgets["labelcharnameclient"]->SetColor(sf::Color(255, 255, 255));

	x_widgets["labelcharnameother"] = new XLabel(data, 0, 0, "");
	x_widgets["labelcharnameother"]->SetBackground(true);
	x_widgets["labelcharnameother"]->SetShadow(true);
	x_widgets["labelcharnameother"]->SetDraw(false);
	x_widgets["labelcharnameother"]->SetAlign("center");
	x_widgets["labelcharnameother"]->SetColor(sf::Color(255, 255, 255));

	// Chat bubbles
	x_widgets["labelbubbleclient"] = new XLabel(data, (x_width/2), 260, "");
	x_widgets["labelbubbleclient"]->SetBackground(true);
	x_widgets["labelbubbleclient"]->SetShadow(true);
	x_widgets["labelbubbleclient"]->SetAlign("center");
	x_widgets["labelbubbleclient"]->SetColor(sf::Color(255, 255, 255));

	x_widgets["labelbubbleother"] = new XLabel(data, 0, 0, "");
	x_widgets["labelbubbleother"]->SetBackground(true);
	x_widgets["labelbubbleother"]->SetShadow(true);
	x_widgets["labelbubbleother"]->SetDraw(false);
	x_widgets["labelbubbleother"]->SetAlign("center");
	x_widgets["labelbubbleother"]->SetColor(sf::Color(255, 255, 255));
	
	// Set the default focus
	x_widgets["textchat"]->SetFocus(true);
}

// Draws the window
void GMain::Draw()
{
	if (m_mapLoaded && x_visible)
	{
		x_app->SetView(x_view);

		// Draw the black bg
		sf::Shape bgfiller = sf::Shape::Rectangle(0, 0, 800, 600, sf::Color(0, 0, 0));
		x_app->Draw(bgfiller);

		// Draw the map
		int charPosX = x_data->GetInt("char-pos-x");
		int charPosY = x_data->GetInt("char-pos-y");

		int startX = charPosX - 30;
		if (startX < 0) startX = 0;
		int startY = charPosY - 30;
		if (startY < 0) startY = 0;
		int finishX = charPosX + 30;
		if (finishX > m_mapSizeX) finishX = m_mapSizeX;
		int finishY = charPosY + 30;
		if (finishY > m_mapSizeY) finishY = m_mapSizeY;

		for (int i=startX; i<finishX; i++)
		{
			for (int j=startY; j<finishY; j++)
			{
				int x = (j * 0.5 * 64) + ((m_mapSizeX-1-i) * 0.5 * 64);
				int y = (j * 0.5 * 32) - ((m_mapSizeX-1-i) * 0.5 * 32);

				int posX = m_vectLayer1[m_mapSizeX-1-i][j] % 10;
				int posY = m_vectLayer1[m_mapSizeX-1-i][j] / 10;

				m_tilesetSprite.SetSubRect(sf::IntRect(posX * 64, posY * 32, (posX * 64)+64, (posY * 32)+32));
				m_tilesetSprite.SetPosition(x+x_offsetX, y+x_offsetY);

				x_app->Draw(m_tilesetSprite);
			}
		}

		// Draws the tile selector
		x_app->Draw(m_selector);

		// Draw the client's character
		sf::Sprite characterSprite;
		characterSprite.SetImage(x_data->GetImage("sprites/base.png"));
		characterSprite.SetSubRect(sf::IntRect(32*x_charDirection-32, 48*x_charAnimationNumber, 32*x_charDirection, 48*x_charAnimationNumber+48));
		characterSprite.SetPosition(x_width/2-16, x_height/2-24);

		x_app->Draw(characterSprite);

		// Client's paperdoll
		for (int i=0; i<12; i++)
		{
			std::stringstream ss2;
			ss2 << "equip_" << i;
			std::stringstream ss;
			ss << "sprites/paperdoll/" << x_data->GetInt(ss2.str()) << ".png";
			sf::Sprite paperdoll;
			paperdoll.SetImage(x_data->GetImage(ss.str()));
			paperdoll.SetSubRect(sf::IntRect(32*x_charDirection-32, 48*x_charAnimationNumber, 32*x_charDirection, 48*x_charAnimationNumber+48));
			paperdoll.SetPosition(x_width/2-16, x_height/2-24);

			x_app->Draw(paperdoll);
		}

		// Chat bubble timeout
		if (m_messageTimeout > 0)
		{
			m_messageTimeout--;
			if (m_messageTimeout == 0)
				x_widgets["labelbubbleclient"]->SetText("", true);
		}

		// Draw the other characters
		x_app->SetView(x_view);
		for(std::map<int, struct Character>::const_iterator it = m_charInfo.begin(); it != m_charInfo.end(); ++it)
		{
			characterSprite.SetSubRect(sf::IntRect(32*it->second.charDirection-32, 48*it->second.charAnimationNumber, 32*it->second.charDirection, 48*it->second.charAnimationNumber+48));
			characterSprite.SetPosition(it->second.xpixel+x_offsetX, it->second.ypixel+x_offsetY);

			x_app->Draw(characterSprite);

			// Paperdoll
			for (int i=0; i<2; i++)
			{
				std::stringstream ss;
				ss << "sprites/paperdoll/" << it->second.equip[i] << ".png";
				sf::Sprite paperdoll;
				paperdoll.SetImage(x_data->GetImage(ss.str()));
				paperdoll.SetSubRect(sf::IntRect(32*it->second.charDirection-32, 48*it->second.charAnimationNumber, 32*it->second.charDirection, 48*it->second.charAnimationNumber+48));
				paperdoll.SetPosition(it->second.xpixel+x_offsetX, it->second.ypixel+x_offsetY);

				x_app->Draw(paperdoll);
			}

			// Charname
			x_widgets["labelcharnameother"]->SetDraw(true);
			x_widgets["labelcharnameother"]->SetPosition(it->second.xpixel+x_offsetX+15, it->second.ypixel+x_offsetY+47);
			x_widgets["labelcharnameother"]->SetText(it->second.charname, false);
			x_widgets["labelcharnameother"]->Draw();
			x_widgets["labelcharnameother"]->SetDraw(false);

			// Chat bubbles
			x_widgets["labelbubbleother"]->SetDraw(true);
			x_widgets["labelbubbleother"]->SetPosition(it->second.xpixel+x_offsetX+15, it->second.ypixel+x_offsetY-20);
			x_widgets["labelbubbleother"]->SetText(it->second.lastMessage, false);
			x_widgets["labelbubbleother"]->Draw();
			x_widgets["labelbubbleother"]->SetDraw(false);
			if (it->second.messageTimeout > 0)
			{
				m_charInfo[it->first].messageTimeout--;
				if (it->second.messageTimeout == 0)
					m_charInfo[it->first].lastMessage = "";
			}
		}

		// Draw the chat box and other widgets
		XWindow::Draw();
	}
}

// Refresh the window
void GMain::CheckState(const sf::Input &input)
{
	// Find the tile the mouse is pointing at
	int sx = input.GetMouseX() - x_offsetX;
    int sy = input.GetMouseY() - x_offsetY;

    int targetX = (sx / 64) - (sy / 32);
    int targetY = (sx / 64) + (sy / 32);

    int x = (targetY * 32) + (targetX * 32);
    int y = (targetY * 16) - (targetX * 16);

    int dx = sx - x;
    int dy = sy - y;

    if (dy < 1) {dy *= -1; dy = 32 - dy; targetX++; targetY--;}

    if (dy < 0.5 * dx - 16) {targetX += 1;}
    else if (dy > 0.5 * dx + 16) {targetX -= 1;}
    else if (dy < -0.5 * dx + 16) {targetY -= 1;}
    else if (dy > -0.5 * dx + 48) {targetY += 1;}

    int pixelx = (targetY * 32) + (targetX * 32);
    int pixely = (targetY * 16) - (targetX * 16);

    m_selector.SetPosition(pixelx+x_offsetX, pixely+x_offsetY);

	// If the character is done the current step / moving
    if (x_charMovementNumber == 0)
    {
		// If the target is not reached
        if (!x_isDoneMoving)
				x_data->NewPacket("nextmove", "", "");

        // Reset the offset
		int charPosX = x_data->GetInt("char-pos-x");
		int charPosY = x_data->GetInt("char-pos-y");

		x_offsetX = 800 / 2 - 32 - ((charPosY * 32) + (charPosX * 32));
		x_offsetY = 400 / 2 - ((charPosY * 16) - (charPosX * 16) - (3*32));

        // Take the next movement, if any
        if (!m_movementQueue.empty())
        {
			if (m_movementQueue.front().dir > 0 && m_movementQueue.front().dir < 9)
			{
				int xvalue[] = {1, 1, 1, 0, -1, -1, -1, 0};
				int yvalue[] = {-1, 0, +1, +1, +1, 0, -1, -1};

				x_data->SetInt("char-pos-x", m_movementQueue.front().x + xvalue[m_movementQueue.front().dir-1]);
				x_data->SetInt("char-pos-y", m_movementQueue.front().y + yvalue[m_movementQueue.front().dir-1]);
				x_charMovementNumber++;
				x_charDirection = m_movementQueue.front().dir;
			}

			m_movementQueue.pop();
        }

    // If in the middle of a movement
    } else {

            int xvalue[] = {0, 4, 4, 4, 0, -4, -4, -4};
			int yvalue[] = {-4, -2, 0, 2, 4, 2, 0, -2};

			x_offsetX -= xvalue[x_charDirection-1];
			x_offsetY -= yvalue[x_charDirection-1];

            int movevalue[] = {8, 8, 16, 8, 8, 8, 16, 8};

			x_charMovementNumber++;
			if (x_charMovementNumber > movevalue[x_charDirection-1])
				x_charMovementNumber = 0;
    }

	// Move somewhere else
	if (input.IsMouseButtonDown(sf::Mouse::Left) && x_charMovementNumber == 0)
	{
		bool t_validTile = true;
		if (targetX == x_lastTargetX && targetY == x_lastTargetY) t_validTile = false;
		if (targetX > m_mapSizeX-1) t_validTile = false;
		if (targetY > m_mapSizeY-1) t_validTile = false;
		if (targetX < 0) t_validTile = false;
		if (targetY < 0) t_validTile = false;

		// If the tile is valid, send request to server
		if (t_validTile)
		{
			std::stringstream ssX, ssY;
			ssX << targetX;
			ssY << targetY;

			x_data->NewPacket("move", ssX.str(), ssY.str());
			x_isDoneMoving = false;
		}

		x_lastTargetX = targetX;
		x_lastTargetY = targetY;
	}

	// Check other characters movement
	for(std::map<int, Character>::iterator it = m_charInfo.begin(); it != m_charInfo.end(); ++it)
	{
		// If the character is done the current step / moving
		if (it->second.charMovementNumber == 0)
		{
			// Take the next movement, if any
			if (!it->second.movementQueue.empty())
			{
				it->second.xpixel = (it->second.y * 0.5 * 64) + (it->second.x * 0.5 * 64) + 16;
				it->second.ypixel = (it->second.y * 0.5 * 32) - (it->second.x * 0.5 * 32) - 22;

				if (it->second.movementQueue.front().dir > 0 && it->second.movementQueue.front().dir < 9 && it->second.movementQueue.front().x > 0 && it->second.movementQueue.front().x < 10000
						&& it->second.movementQueue.front().y > 0 && it->second.movementQueue.front().y < 10000)
				{
					int xvalue[] = {1, 1, 1, 0, -1, -1, -1, 0};
					int yvalue[] = {-1, 0, +1, +1, +1, 0, -1, -1};

					it->second.x = it->second.movementQueue.front().x + xvalue[it->second.movementQueue.front().dir-1];
					it->second.y = it->second.movementQueue.front().y + yvalue[it->second.movementQueue.front().dir-1];

					it->second.charMovementNumber++;
					it->second.charDirection = it->second.movementQueue.front().dir;
				}

				it->second.movementQueue.pop();
			}

		// If in the middle of a movement
		} else {

			int xvalue[] = {0, 4, 4, 4, 0, -4, -4, -4};
			int yvalue[] = {-4, -2, 0, 2, 4, 2, 0, -2};

			it->second.xpixel += xvalue[it->second.charDirection-1];
			it->second.ypixel += yvalue[it->second.charDirection-1];

			int movevalue[] = {8, 8, 16, 8, 8, 8, 16, 8};

			it->second.charMovementNumber++;
			if (it->second.charMovementNumber > movevalue[it->second.charDirection-1])
				it->second.charMovementNumber = 0;
		}
	}

	// Check the state of the widgets
    XWindow::CheckState(input);
}

// Check the events (key press)
void GMain::CheckEvent(const sf::Event &Event)
{
	XWindow::CheckEvent(Event);

	// Pressing enter
	if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Return))
	{
		if (x_widgets["textchat"]->focus() == XG_FOC_YES)
		{
			if (x_widgets["textchat"]->GetText() != "")
			{
				if (x_widgets["textchat"]->GetText()[0] != '/' && x_widgets["textchat"]->GetText()[0] != '!')
				{
					x_widgets["labelbubbleclient"]->SetText(x_data->GetString("charname")+" : "+x_widgets["textchat"]->GetText(), true);
					m_messageTimeout = 60 * 6;
				}

				x_data->NewPacket("message", "", x_widgets["textchat"]->GetText());
				x_widgets["textchat"]->SetText("");

				
			} else
				x_widgets["textchat"]->SetFocus(false);
			
		} else {

			x_widgets["textchat"]->SetFocus(true);
		}
	}

	// Chat history
	x_widgets["scrollhistory"]->CheckEvent(Event);

	if (x_widgets["textchat"]->focus() == XG_FOC_NO)
	{
		if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::I))
			x_data->NewQueue("ui", "displaywindow", "inventory");
	}
}

// Check the queries of the widgets
void GMain::CheckQuery()
{
	if (!x_data->queueEmpty("main"))
	{
		functions function = x_data->nextQueue("main");

		// Take the next node
		if (function.argument == "move")
		{
			std::string currentVariable;
			int lastCheck = 0;
			int checkPosition = 0;
			bool done = false;
			int pos = 0;

			Node node;

			while (lastCheck < function.variable.size()-1)
			{
				// Check for the variable value
				done = false;
				while (!done)
				{
					if (function.variable[checkPosition] == ';')
					{
						currentVariable = function.variable.substr(lastCheck, (checkPosition - lastCheck));
						
						switch (pos)
						{
							case 0:
								node.dir = atoi(currentVariable.c_str());
								break;

							case 1:
								node.x = atoi(currentVariable.c_str());
								break;

							case 2:
								node.y = atoi(currentVariable.c_str());
								break;
						}

						lastCheck = checkPosition + 1;
						done = true;
						pos++;
					}

					checkPosition++;
				}
			}

			m_movementQueue.push(node);
		}

		// When the client is done moving
		if (function.argument == "donemove")
		{
			x_isDoneMoving = true;
		}

		// Load the map
		if (function.argument == "loadmap")
		{
			m_mapLoaded = false;
			m_charInfo.clear();

			while (!m_movementQueue.empty())
				m_movementQueue.pop();

			x_widgets["labelcharnameclient"]->SetText(x_data->GetString("charname"), true);

			x_data->NewLog("Loading map...");

			std::string t_musicname;

			std::stringstream t_stream;
			t_stream << "../../data/maps/" << x_data->GetInt("map-id") << ".txt";
			std::string t_string = t_stream.str();

			// Open and read the map file
			std::ifstream f_map(t_string.c_str(), std::ios::in);

			if(f_map)
			{
				f_map >> m_mapName >> t_musicname >> m_mapTileset >> m_mapSizeX >> m_mapSizeY;

				// Resize the vectors
				m_vectLayer1.resize(m_mapSizeX,std::vector<int>(m_mapSizeY,0));
				m_vectLayer2.resize(m_mapSizeX,std::vector<int>(m_mapSizeY,0));
				m_vectLayer3.resize(m_mapSizeX,std::vector<int>(m_mapSizeY,0));
				m_vectLayer4.resize(m_mapSizeX,std::vector<int>(m_mapSizeY,0));

				// Put the 3 layers into their vectors
				for(int j=0; j<m_mapSizeY; j++)
					for(int i=0; i<m_mapSizeX; i++)
						f_map >> m_vectLayer1[i][j];

				for(int j=0; j<m_mapSizeY; j++)
					for(int i=0; i<m_mapSizeX; i++)
						f_map >> m_vectLayer2[i][j];

				for(int j=0; j<m_mapSizeY; j++)
					for(int i=0; i<m_mapSizeX; i++)
						f_map >> m_vectLayer3[i][j];

				for(int j=0; j<m_mapSizeY; j++)
					for(int i=0; i<m_mapSizeX; i++)
						f_map >> m_vectLayer4[i][j];

				f_map.close();
			}

			// Open and read the tileset configuration file
			std::stringstream t_stream2;
			t_stream2 << "../../data/tilesets/" << m_mapTileset << ".txt";
			std::string t_string2 = t_stream2.str();

			std::ifstream f_tileset(t_string2.c_str(), std::ios::in);

			if(f_tileset)
			{
				int t_numberOfTiles;
				f_tileset >> t_numberOfTiles;

				m_vectLayerPassability.resize(t_numberOfTiles);
				for (int i=0; i<t_numberOfTiles; i++)
				{
					int tmp;
					f_tileset >> tmp;
					m_vectLayerPassability[i] = tmp;
				}

				f_tileset.close();
			}

			// Open the tileset .png file and load the sf::Image and sf::Sprite with it
			std::stringstream t_streamTs;
			t_streamTs << "tilesets/" << m_mapTileset << ".png";
			std::string t_stringTs = t_streamTs.str();

			m_tilesetSprite.SetImage(x_data->GetImage(t_stringTs));

			std::stringstream t_stream3;
			t_stream3 << "../../data/musics/" << t_musicname << ".ogg";
			std::string t_string3 = t_stream3.str();

			m_bgmusic.OpenFromFile(t_string3);
			m_bgmusic.SetLoop(true);
			m_bgmusic.Play();

			while (!m_movementQueue.empty())
				m_movementQueue.pop();

			x_data->NewLog("Done loading map.");

			m_mapLoaded = true;
		}

		// When a character logs in or change in current map, put it in the map
		if (function.argument == "charin")
		{
			std::string currentVariable;
			int lastCheck = 0;
			int checkPosition = 0;
			bool done = false;
			int pos = 0;

			int id;
			Character newClient;

			while (lastCheck < function.variable.size()-1)
			{
				// Check for the variable value
				done = false;
				while (!done)
				{
					if (function.variable[checkPosition] == ';')
					{
						currentVariable = function.variable.substr(lastCheck,  (checkPosition - lastCheck));
						
						switch (pos)
						{
							case 0:
								id = atoi(currentVariable.c_str());
								break;

							case 1:
								newClient.x = atoi(currentVariable.c_str());
								break;

							case 2:
								newClient.y = atoi(currentVariable.c_str());
								break;

							case 3:
								newClient.charname = currentVariable;
								break;

							case 4:
								newClient.equip.push_back(atoi(currentVariable.c_str()));
								break;

							case 5:
								newClient.equip.push_back(atoi(currentVariable.c_str()));
								break;
						}

						lastCheck = checkPosition + 1;
						done = true;
						pos++;
					}

					checkPosition++;
				}
			}

			newClient.xpixel = (newClient.y * 0.5 * 64) + (newClient.x * 0.5 * 64) + 16;
			newClient.ypixel = (newClient.y * 0.5 * 32) - (newClient.x * 0.5 * 32) - 22;
			newClient.charDirection = 5;
			newClient.charAnimationNumber = 0;
			newClient.charMovementNumber = 0;
			m_charInfo[id] = newClient;
		}

		// When a character logs out or change map, erase it
		if (function.argument == "charout")
		{
			int id = atoi(function.variable.c_str());
			m_charInfo.erase(id);

			x_widgets["labelcharnameother"]->SetText("", true);
		}

		// When a character moves
		if (function.argument == "charmove")
		{
			std::string currentVariable;
			int lastCheck = 0;
			int checkPosition = 0;
			bool done = false;
			int pos = 0;

			int id;
			Node node;
			node.x = 0;
			node.y = 0;

			while (lastCheck < function.variable.size()-1)
			{
				// Check for the variable value
				done = false;
				while (!done)
				{
					if (function.variable[checkPosition] == ';')
					{
						currentVariable = function.variable.substr(lastCheck,  (checkPosition - lastCheck));
						
						switch (pos)
						{
							case 0:
								id = atoi(currentVariable.c_str());
								break;

							case 1:
								node.dir = atoi(currentVariable.c_str());
								break;

							case 2:
								node.x = atoi(currentVariable.c_str());
								break;

							case 3:
								node.y = atoi(currentVariable.c_str());
								break;
						}

						lastCheck = checkPosition + 1;
						done = true;
						pos++;
					}

					checkPosition++;
				}

				if (node.x != 0 && node.y != 0)
					m_charInfo[id].movementQueue.push(node);

				std::cout << "CHAR MOVEMENT : " << node.x << "," << node.y << std::endl;
			}
		}
	}

	// If a new message was received
	if (!x_data->queueEmpty("message"))
	{
		functions function = x_data->nextQueue("message");

		if (function.argument == "server")
			x_widgets["scrollhistory"]->NewLine(function.variable, 0);
		else if (function.argument == "global")
			x_widgets["scrollhistory"]->NewLine(function.variable, 2);
		else if (function.argument == "whisper")
			x_widgets["scrollhistory"]->NewLine(function.variable, 3);
		else
			x_widgets["scrollhistory"]->NewLine(function.argument+" : "+function.variable, 1);

		// Put the message in the character vector
		for(std::map<int, struct Character>::const_iterator it = m_charInfo.begin(); it != m_charInfo.end(); ++it)
		{
			if (it->second.charname == function.argument)
			{
				m_charInfo[it->first].lastMessage = function.argument+" : "+function.variable;
				m_charInfo[it->first].messageTimeout = 60 * 6;
				break;
			}
		}
	}
}
